/*
 * @Author: 0x9DEFA478
 * @Date: 2025-10-05 19:03:02
 * @LastEditTime: 2025-10-06 01:16:26
 * @LastEditors: 0x9DEFA478
 * @Description: 
 * 
 */
#include "./Device_WINUSB.hpp"
#if defined(_WIN32)
#include "../../Tree/H_Tree_RedBlack.hpp"
#include "../../Utility/H_String.hpp"
#include "./DeviceEnum.hpp"
#include "./GUID.hpp"
#include "./Encoding.hpp"
#include "./Handle.hpp"
#include "./Error.hpp"
#include <windows.h>
#include <stdexcept>
#endif




namespace SystemAPI{

  namespace Windows{

#if defined(_WIN32)
    namespace{

      class WINUSB_PIPE{
        public:
          WINUSB_PIPE()noexcept:pipeID(0),maxPacketSize(0),timeout(0){}
          WINUSB_PIPE(UCHAR PIPE_ID,int MaxPacketSize)noexcept:pipeID(PIPE_ID),maxPacketSize(MaxPacketSize),timeout(0){}
          ~WINUSB_PIPE()noexcept=default;

          int MaxPacketSize()const noexcept{return maxPacketSize;}
          UCHAR PIPE_ID()const noexcept{return pipeID;}
          DWORD Timeout()const noexcept{return timeout;}

          DWORD SetTimeout(DWORD timeout)noexcept{return this->timeout=timeout;}

          void BufferResize(H::usize Size){
            if(buffer.Length()<Size){
              buffer=buffer.Clone(Size);
            }
          }

          UCHAR* Buffer()noexcept{return buffer.GetPointer();}

        private:
          UCHAR pipeID;
          int maxPacketSize;
          DWORD timeout;
          H::Pointer<UCHAR> buffer;
      };

      struct Device_WINUSB_Data{
        
        Handle handle;
        Handle handle_interface;

        H::Pointer<WINUSB_PIPE> InputPIPE_Array;
        H::Pointer<WINUSB_PIPE> OutputPIPE_Array;

        static constexpr Device_WINUSB_Data* Cast(void* pdata){return static_cast<Device_WINUSB_Data*>(pdata);}
      };
    }

    Device_WINUSB::Device_WINUSB():privateData(nullptr){}

    Device_WINUSB::Device_WINUSB(const std::string& Path):path(Path),privateData(nullptr){}

    Device_WINUSB::~Device_WINUSB()noexcept{
      if(privateData!=nullptr){
        delete Device_WINUSB_Data::Cast(privateData);
      }
    }

    Device_WINUSB::Device_WINUSB(Device_WINUSB&& device_winusb)noexcept:path(std::move(device_winusb.path)),privateData(device_winusb.privateData){
      device_winusb.privateData=nullptr;
    }

    Device_WINUSB& Device_WINUSB::operator=(Device_WINUSB&& device_winusb)noexcept{
      if(privateData!=nullptr){
        delete Device_WINUSB_Data::Cast(privateData);
      }
      path=std::move(device_winusb.path);
      privateData=device_winusb.privateData;
      device_winusb.privateData=nullptr;
      return *this;
    }

    int Device_WINUSB::InputMaxPacketSize(H::byte Index)const noexcept{
      if(privateData==nullptr){
        return -1;
      }
      auto& inputPIPE_List=Device_WINUSB_Data::Cast(privateData)->InputPIPE_Array;
      if(Index>=inputPIPE_List.Length()){
        return -1;
      }
      return inputPIPE_List[Index].MaxPacketSize();
    }

    int Device_WINUSB::OutputMaxPacketSize(H::byte Index)const noexcept{
      if(privateData==nullptr){
        return -1;
      }
      auto& outputPIPE_List=Device_WINUSB_Data::Cast(privateData)->OutputPIPE_Array;
      if(Index>=outputPIPE_List.Length()){
        return -1;
      }
      return outputPIPE_List[Index].MaxPacketSize();
    }

    void Device_WINUSB::Open(bool InfoMode){
      using namespace SystemAPI;

      if(privateData!=nullptr){
        return;
      }

      auto wpath=Windows::TextConvertToWideString(CP_UTF8,path);
      DWORD dwDesiredAccess=GENERIC_READ|GENERIC_WRITE;
      DWORD dwShareMode=0;
      if(InfoMode){
        dwDesiredAccess=0;
        dwShareMode=FILE_SHARE_READ|FILE_SHARE_WRITE;
      }
      auto handle=Windows::Handle(CreateFileW(&wpath[0],dwDesiredAccess,dwShareMode,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL));

      if(handle.GetHandle()==INVALID_HANDLE_VALUE){
        throw std::runtime_error("\""+path+"\" 打开失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
      }


      WINUSB_INTERFACE_HANDLE winusb_interface_handle=NULL;
      if(FALSE==WinUsb_Initialize(handle.GetHandle(),&winusb_interface_handle)){
        throw std::runtime_error("\""+path+"\" WinUsb_Initialize失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
      }
      auto handle_interface=Windows::Handle((HANDLE)winusb_interface_handle,[](HANDLE handle){WinUsb_Free((WINUSB_INTERFACE_HANDLE)handle);});

      USB_INTERFACE_DESCRIPTOR interface_desc;
      if(FALSE==WinUsb_QueryInterfaceSettings(handle_interface.GetHandle(),0,&interface_desc)){
        throw std::runtime_error("\""+path+"\" 获取接口描述符失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
      }

      //获取字符串描述符

      if((!InfoMode)&&(interface_desc.bAlternateSetting!=0)){//具有复用设置的接口 使用设置0
        if(FALSE==WinUsb_SetCurrentAlternateSetting(handle_interface.GetHandle(),0)){
          throw std::runtime_error("\""+path+"\" 设置接口复用设置失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
        }
      }

      H::Tree::RedBlack<H::byte,WINUSB_PIPE_INFORMATION> inputPipeTree;
      H::Tree::RedBlack<H::byte,WINUSB_PIPE_INFORMATION> outputPipeTree;

      for(int i=0,length=interface_desc.bNumEndpoints;i<length;i++){
        WINUSB_PIPE_INFORMATION info;
        if(FALSE==WinUsb_QueryPipe(handle_interface.GetHandle(),0,i,&info)){
          throw std::runtime_error("\""+path+"\" 获取第"+std::to_string(i)+"个PIPE信息失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
        }

        if(info.PipeType!=UsbdPipeTypeBulk){
          continue;
        }

        H::byte EPIndex=info.PipeId&0x0FU;
        if((info.PipeId&0x80U)==0){
          //OutEP
          outputPipeTree.Insert(EPIndex,info);
        }else{
          //InEP
          inputPipeTree.Insert(EPIndex,info);
        }
      }

      auto pdata=new Device_WINUSB_Data;

      if(!InfoMode){

        auto SetPipePolicyBool=[this](WINUSB_INTERFACE_HANDLE InterfaceHandle,UCHAR PipeID,ULONG PolicyType,const std::string& PolicyTypeName,WINBOOL Value){
          if(FALSE==WinUsb_SetPipePolicy(InterfaceHandle,PipeID,PolicyType,sizeof(Value),&Value)){
            throw std::runtime_error("\""+path+"\" 设置PIPE"+H::String::Format("%0X2",PipeID)+"的"+PolicyTypeName+"失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
          }
        };

        auto SetPipePolicyDWORD=[this](WINUSB_INTERFACE_HANDLE InterfaceHandle,UCHAR PipeID,ULONG PolicyType,const std::string& PolicyTypeName,DWORD Value){
          if(FALSE==WinUsb_SetPipePolicy(InterfaceHandle,PipeID,PolicyType,sizeof(Value),&Value)){
            throw std::runtime_error("\""+path+"\" 设置PIPE"+H::String::Format("%0X2",PipeID)+"的"+PolicyTypeName+"失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
          }
        };

        {
          pdata->InputPIPE_Array=H::Pointer<WINUSB_PIPE>::Array(inputPipeTree.Count());

          auto dst=pdata->InputPIPE_Array.begin();
          for(auto& Item:inputPipeTree){
            *dst=WINUSB_PIPE(Item.Data().PipeId,Item.Data().MaximumPacketSize);
            ++dst;
            WinUsb_FlushPipe(handle_interface.GetHandle(),Item.Data().PipeId);

            SetPipePolicyBool(handle_interface.GetHandle(),Item.Data().PipeId,AUTO_CLEAR_STALL,"AUTO_CLEAR_STALL",FALSE);
            SetPipePolicyDWORD(handle_interface.GetHandle(),Item.Data().PipeId,PIPE_TRANSFER_TIMEOUT,"PIPE_TRANSFER_TIMEOUT",1000);
            SetPipePolicyBool(handle_interface.GetHandle(),Item.Data().PipeId,IGNORE_SHORT_PACKETS,"IGNORE_SHORT_PACKETS",FALSE);
            SetPipePolicyBool(handle_interface.GetHandle(),Item.Data().PipeId,ALLOW_PARTIAL_READS,"ALLOW_PARTIAL_READS",TRUE);
            SetPipePolicyBool(handle_interface.GetHandle(),Item.Data().PipeId,AUTO_FLUSH,"AUTO_FLUSH",FALSE);
            SetPipePolicyBool(handle_interface.GetHandle(),Item.Data().PipeId,RAW_IO,"RAW_IO",FALSE);
            SetPipePolicyBool(handle_interface.GetHandle(),Item.Data().PipeId,RESET_PIPE_ON_RESUME,"RESET_PIPE_ON_RESUME",FALSE);
          }
        }
        {
          pdata->OutputPIPE_Array=H::Pointer<WINUSB_PIPE>::Array(outputPipeTree.Count());

          auto dst=pdata->OutputPIPE_Array.begin();
          for(auto& Item:outputPipeTree){
            *dst=WINUSB_PIPE(Item.Data().PipeId,Item.Data().MaximumPacketSize);
            ++dst;
            WinUsb_FlushPipe(handle_interface.GetHandle(),Item.Data().PipeId);

            SetPipePolicyBool(handle_interface.GetHandle(),Item.Data().PipeId,SHORT_PACKET_TERMINATE,"SHORT_PACKET_TERMINATE",FALSE);
            SetPipePolicyDWORD(handle_interface.GetHandle(),Item.Data().PipeId,PIPE_TRANSFER_TIMEOUT,"PIPE_TRANSFER_TIMEOUT",1000);
            SetPipePolicyBool(handle_interface.GetHandle(),Item.Data().PipeId,RESET_PIPE_ON_RESUME,"RESET_PIPE_ON_RESUME",FALSE);
          }
        }
      }

      pdata->handle=std::move(handle);
      pdata->handle_interface=std::move(handle_interface);

      privateData=static_cast<void*>(pdata);
    }

    void Device_WINUSB::Close()noexcept{
      if(privateData!=nullptr){
        delete Device_WINUSB_Data::Cast(privateData);
        privateData=nullptr;
      }
    }

    int Device_WINUSB::Read(H::byte Index,void* Buffer,int BufferSize,int Timeout){
      if(privateData==nullptr){
        return 0;
      }

      auto pdata=Device_WINUSB_Data::Cast(privateData);
      if(Index>=pdata->InputPIPE_Array.Length()){
        throw std::runtime_error("Index超过读取PIPE索引范围");
      }

      auto& pipe=pdata->InputPIPE_Array[Index];

      if(pipe.Timeout()!=(DWORD)Timeout){
        DWORD Value=(DWORD)Timeout;
        if(FALSE==WinUsb_SetPipePolicy(pdata->handle_interface.GetHandle(),pipe.PIPE_ID(),PIPE_TRANSFER_TIMEOUT,sizeof(Value),&Value)){
          throw std::runtime_error("\""+path+"\" 设置PIPE"+H::String::Format("%0X2",pipe.PIPE_ID())+"的PIPE_TRANSFER_TIMEOUT失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
        }
        pipe.SetTimeout(Value);
      }

      ULONG LengthTransferred;
      if(FALSE==WinUsb_ReadPipe(pdata->handle_interface.GetHandle(),pipe.PIPE_ID(),(PUCHAR)Buffer,BufferSize,&LengthTransferred,NULL)){
        auto err=GetLastError();
        if(err==ERROR_SEM_TIMEOUT){
          return -1;
        }
        throw std::runtime_error("\""+path+"\" PIPE"+H::String::Format("%0X2",pipe.PIPE_ID())+"写入失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
      }

      return LengthTransferred;
    }

    int Device_WINUSB::Write(H::byte Index,const void* Data,int DataLength,int Timeout){
      if(privateData==nullptr){
        return 0;
      }

      auto pdata=Device_WINUSB_Data::Cast(privateData);
      if(Index>=pdata->OutputPIPE_Array.Length()){
        throw std::runtime_error("Index超过读取PIPE索引范围");
      }

      auto& pipe=pdata->OutputPIPE_Array[Index];

      if(pipe.Timeout()!=(DWORD)Timeout){
        DWORD Value=(DWORD)Timeout;
        if(FALSE==WinUsb_SetPipePolicy(pdata->handle_interface.GetHandle(),pipe.PIPE_ID(),PIPE_TRANSFER_TIMEOUT,sizeof(Value),&Value)){
          throw std::runtime_error("\""+path+"\" 设置PIPE"+H::String::Format("%0X2",pipe.PIPE_ID())+"的PIPE_TRANSFER_TIMEOUT失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
        }
        pipe.SetTimeout(Value);
      }

      ULONG LengthTransferred;
      pipe.BufferResize(DataLength);
      auto Buffer=pipe.Buffer();
      for(int i=0;i<DataLength;i++){
        Buffer[i]=((const H::byte*)Data)[i];
      }
      if(FALSE==WinUsb_WritePipe(pdata->handle_interface.GetHandle(),pipe.PIPE_ID(),Buffer,DataLength,&LengthTransferred,NULL)){
        auto err=GetLastError();
        if(err==ERROR_SEM_TIMEOUT){
          return -1;
        }
        throw std::runtime_error("\""+path+"\" PIPE"+H::String::Format("%0X2",pipe.PIPE_ID())+"写入失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
      }

      return LengthTransferred;
    }

    std::list<std::string> Device_WINUSB::FindDeviceList(const std::string& GUID){
      return SystemAPI::Windows::EnumDevice_ByGUID(SystemAPI::Windows::GUID(GUID).GetGUID());
    }

#endif

  }
}



